/*
board-games:	An educational framework for board games programming.
    Copyright (C) 2008  Daniel Gutson, FuDePAN
    
    This file is part of board-games.

    board-games is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    board-games is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
    
*/

#ifdef POSIX_QUEUES
#include <mqueue.h>
#include <sstream>
#include <string>

class MQueue
{
    bool created;
    mqd_t mqueue;
    std::string name;
    size_t mq_msg_size;
    char* mq_receive_buffer;
    
    static const int OK = 0;
    static const unsigned int PRIO = 1;
public:
    MQueue() : created(false), mq_msg_size(0), mq_receive_buffer(NULL) {}

    ~MQueue()
    {
        if (created)
            mq_close(mqueue);
            
        delete [] mq_receive_buffer;
    }
    
    const std::string& get_name() const  { return name; }
    
    void create()
    {
        static size_t QUEUE_ID;
        QUEUE_ID++;
        
        std::stringstream ss;
        ss << "/Q" << QUEUE_ID;
        name = ss.str();
        mqueue = mq_open(name.c_str(), O_CREAT | O_RDWR);
        created = true;
    }
    
    void open(const std::string& mqname)
    {
        name = mqname;
        mqueue = mq_open(name.c_str(), O_RDWR);        
    }

    void send_message(void* msgdata, size_t size)
    {
        if (mq_send(mqueue, msgdata, size, PRIO) != OK)
            throw "MQSendError"; // DANGER! Should be std::exception
    }
    
    void receive_message(void*& msgdata, size_t& size)
    {
        if (mq_msg_size == 0)
        {
            mq_attr attr;
            mq_get_attr(mqueue, &attr);
            mq_msg_size = attr.mq_msgsize;
            mq_receive_buffer = new char[mq_msg_size];
        }
        
        int received_bytes;

        received_bytes = mq_receive(mqueue, buffer, mq_msg_size, NULL);
        
        if (received_bytes <= 0)
            throw "MQReceiveError";
        
        size = size_t(received_bytes);
        
        msgdata = reinterpret_cast<void*>(mq_receive_buffer);
    }

};

#else
#include <sys/msg.h>
#include <errno.h>

class MQueue
{
    bool created;
    int mqueue;
    size_t id;
    size_t mq_msg_size;
    char* buffer;
    static size_t QUEUE_ID;
    
    static const int OK = 0;
    
    void check_buffer()
    {
        if (mq_msg_size == 0)
        {
            msqid_ds ds;
            msgctl(mqueue, IPC_STAT, &ds);
            mq_msg_size = ds.msg_qbytes;
            buffer = new char[mq_msg_size];
            *reinterpret_cast<long*>(buffer) = 1; // the mtype;
        }
    }
    
public:
    MQueue() : created(false), mq_msg_size(0), buffer(NULL) {}

    ~MQueue()
    {
        if (created)
        {std::cerr << "removing mqueue\n";
            msgctl(mqueue, IPC_RMID, NULL);
        }
            
        delete [] buffer;
    }
    
    size_t get_id() const  { return id; }
    
    void create()
    {
        id = QUEUE_ID;
        QUEUE_ID++;
        key_t key = ftok("/tmp", id);
        if (key == -1)
            throw "ftok error during mq create";
        
        mqueue = msgget(key, 0666 | IPC_CREAT);
        if (mqueue == -1)
            throw "msgget error during mq create";

        created = true;
    }
    
    void open(size_t mqid)
    {
        id = mqid;
        key_t key = ftok("/tmp", id);
        
        if (key == -1)
            throw "ftok error during mq open";

        mqueue = msgget(key, 0);        
        if (mqueue == -1)
            throw "msgget error during mq open";

    }

    void send_message(void* msgdata, size_t size)
    {
        check_buffer();
        std::cerr << "sending " << size << " bytes thru " << mqueue << "\n";
        memcpy(buffer+sizeof(long), msgdata, size); // skip the mtype
        if (msgsnd(mqueue, buffer, size, 0) != OK)
            throw "MQSendError"; // DANGER! Should be std::exception
    }
    
    void receive_message(void*& msgdata, size_t& size)
    {        
        int received_bytes;
        
        check_buffer();

        received_bytes = msgrcv(mqueue, buffer, mq_msg_size, 0, 0);
        
        if (received_bytes <= 0)
            throw "MQReceiveError";
        
        size = size_t(received_bytes);
        
        msgdata = reinterpret_cast<void*>(buffer+sizeof(long)); // skip the mtype
    }

};

#endif
